﻿using System;
using System.Windows.Forms;
//using PluginInterface;
using System.Drawing;
using Qurihara;
using System.Collections;
using System.Xml;
using Qurihara.Anm;
//using System.IO;
using OSC.NET;
using System.Threading;
using System.Net;
using Qurihara.QBasic;
using System.Text;
using System.Collections.Generic;
using Microsoft.Ink;
using KotodamaPlugin;

using System.Configuration;

namespace Qurihara.OSCCom
{
    public class ComForDisplay// :OSCComCore
    {
        #region event for manager
        public event ViewMovedReceivedEventHandler ViewMovedReceived = null;
        public delegate void ViewMovedReceivedEventHandler(object sender, ViewMovedReceivedEventArgs e);
        public class ViewMovedReceivedEventArgs : EventArgs
        {
            public ViewMovedReceivedEventArgs(string name, Rectangle rect)
            {
                Name = name;
                ViewRectangle = rect;
            }
            public string Name;
            public Rectangle ViewRectangle;
        }
        public event StrokeElementListCreatedEventHandler StrokeElementListCreated = null;
        public delegate void StrokeElementListCreatedEventHandler(object sender, StrokeElementListCreatedEventArgs e);
        public class StrokeElementListCreatedEventArgs : EventArgs
        {
            public StrokeElementListCreatedEventArgs(List<StrokeElement> sList)
            {
                StrokeElementList = sList;
            }
            public List<StrokeElement> StrokeElementList;
        }
        public event NewPersonalInkNameReceivedEventHandler NewPersonalInkNameReveived = null;
        public delegate void NewPersonalInkNameReceivedEventHandler(object sender, NewPersonalInkNameReceivedEventArgs e);
        public class NewPersonalInkNameReceivedEventArgs : EventArgs
        {
            public NewPersonalInkNameReceivedEventArgs(string name)
            {
                Name = name;
            }
            public string Name;
        }
#endregion

        private Qurihara.QBasic.DelayedActivator delayedActivatorForAutoScroll;
        private Qurihara.QBasic.DelayedActivator delayedActivatorForSync;
        private Queue<MessageEventArgs> receivedMessageEventQueue;
        private bool syncReady = true;

        public static int InkDumpingFactor = 2;

        protected IKotodamaBase kotodama;

        private object stackedView = null;

        private static bool sendLogView = false;
        private Rectangle latestRectangleForSendLogView;
        private static bool invalidated = false;
        private Rectangle latestRectangle;
        private static Mutex mutInvalidated = new Mutex();
        private ViewSnapList viewSnapList = null;

        private System.Windows.Forms.Timer inkTimer;
        //private List<Stroke> sendingStrokeList;
        private List<StrokeElement> sendingStrokeList;

        private List<StrokeElement> sendingEraseStrokeList;

        private bool sendView = false;
        private bool sendInkImmediately = false;
        private bool enableAutoScroll = true;
        private bool canChangeAutoScroll = true;
        private bool sendToAllInk = false;
        private bool sendPersonalInk = false;
        private bool receivePersonalInk = false;

        private int snapOffset = 0;
        private bool overview = false;
        private bool sendLog = false;
        private bool manager = true;

        //for overview
        private System.Windows.Forms.Timer overviewTimer;
        private Rectangle prevRectangle = new Rectangle(0, 0, 0, 0);

        protected IComCore iComCore;
        System.Windows.Forms.Timer timer;

        public bool EnableAutoScroll
        {
            get { return enableAutoScroll; }
            set
            {
                if (canChangeAutoScroll)
                {
                    mutInvalidated.WaitOne();
                    enableAutoScroll = value;
                    if (enableAutoScroll)
                    {
                        if (stackedView != null)
                        {
                            if (stackedView is Rectangle)
                            {
                                Rectangle r = Rectangle.Intersect((Rectangle)stackedView, kotodama.Presentation.CurrentSlide.View.ViewRectangle);
                                if (r.Width * r.Height > 0)
                                {
                                    kotodama.Presentation.CurrentSlide.View.ViewRectangle
                                        = (Rectangle)stackedView;
                                }
                                else
                                {
                                    ViewSnap vs = new ViewSnap("", (Rectangle)stackedView, kotodama.Presentation.CurrentSlide.View);
                                    vs.GoLinear();
                                }
                            }
                            else if (stackedView is int)
                            {
                                if (viewSnapList != null && viewSnapList.Count > (int)stackedView && (int)stackedView >= 0)
                                {
                                    //viewSnapList.Go((int)stackedView);
                                    viewSnapList.GoLinear((int)stackedView);
                                }
                            }
                            stackedView = null;
                        }
                    }
                    mutInvalidated.ReleaseMutex();
                }
            }
        }

        #region properties
        //debug
        public bool _sendView
        {
            get { return sendView; }
            set { sendView = value; }
        }
        public bool _sendInkImmediately
        {
            get { return sendInkImmediately; }
            set { sendInkImmediately = value; }
        }
        public bool _canChangeAutoScroll
        {
            get { return canChangeAutoScroll; }
            set { canChangeAutoScroll = value; }
        }
        public bool _sendToAllInk
        {
            get { return sendToAllInk; }
            set { sendToAllInk = value; }
        }
        public bool _sendPersonalInk
        {
            get { return sendPersonalInk; }
            set { sendPersonalInk = value; }
        }
        public bool _recievePersonalInk
        {
            get { return receivePersonalInk; }
            set { receivePersonalInk = value; }
        }

        public int _snapOffset
        {
            get { return snapOffset; }
            set { snapOffset = value; }
        }

        public bool _overview
        {
            get { return overview; }
            set { overview = value; }
        }

        public bool _sendLog
        {
            get { return sendLog; }
            set { sendLog = value; }
        }

        public bool _manager
        {
            get { return manager; }
            set { manager = value; }
        }

        public string Name
        {
            get { return iComCore.Name; }
            set { iComCore.Name = value; }
        }
        public string Ip
        {
            get { return iComCore.Ip; }
        }
        public int MPort
        {
            get { return iComCore.MPort; }
        }

        //public bool IsConnected
        //{
        //    get
        //    {
        //        if (iComCore != null) return iComCore.IsConnected;
        //        return false;
        //    }
        //}

        #endregion

        public ComForDisplay(IKotodamaBase _kotodama,IComCore iComCore)
            : base()
        {
            kotodama = _kotodama;
            this.iComCore = iComCore;
            iComCore.MessageReceived += new MessageEventHandler(iComCore_MessageReceived);

            sendView = Properties.Settings.Default.SendView;
            canChangeAutoScroll = Properties.Settings.Default.CanChangeAutoScroll;
            sendInkImmediately = Properties.Settings.Default.SendInkImmediately;
            sendToAllInk = Properties.Settings.Default.SendToAllInk;
            sendPersonalInk = Properties.Settings.Default.SendPersonalInk;
            receivePersonalInk = Properties.Settings.Default.ReceivePersonalInk;

            snapOffset = Properties.Settings.Default.SnapOffset;
            overview = Properties.Settings.Default.Overview;
            sendLog = Properties.Settings.Default.SendLog;
            manager = Properties.Settings.Default.Manager;

            delayedActivatorForAutoScroll = new DelayedActivator();
            delayedActivatorForAutoScroll.MilliSecToActivate = 10000;
            delayedActivatorForAutoScroll.Activated += new EventHandler(delayedActivatorForAutoScroll_Activated);
            delayedActivatorForAutoScroll.StartAndWaitForUpdate();

            receivedMessageEventQueue = new Queue<MessageEventArgs>();
            delayedActivatorForSync = new DelayedActivator();
            delayedActivatorForSync.MilliSecToActivate = 3000;// 1000;
            delayedActivatorForSync.Activated += new EventHandler(delayedActivatorForSync_Activated);
            delayedActivatorForSync.StartAndWaitForUpdate();
        }

        void ViewWriteMode_ViewMovedCreated(object sender, KotodamaPlugin.ViewWritePlugin.ViewWriteMode.ViewMovedEventArgs e)
        {
            //受動的ではなく、ツールにより視点移動したとき（viewsnapchangeはのぞく)
            //EnableAutoScroll = false;
            UpdateDelayedActivatorForAutoScroll();
        }

        private void UpdateDelayedActivatorForAutoScroll()
        {
            EnableAutoScroll = false;
            mutInvalidated.WaitOne();
            syncReady = false;
            mutInvalidated.ReleaseMutex();
            delayedActivatorForAutoScroll.Update();
            delayedActivatorForSync.Update();
        }

        void delayedActivatorForSync_Activated(object sender, EventArgs e)
        {
            mutInvalidated.WaitOne();
            syncReady = true;
            mutInvalidated.ReleaseMutex();
            ParseEvent();
        }

        void delayedActivatorForAutoScroll_Activated(object sender, EventArgs e)
        {
            Delegate d = (MethodInvoker)delegate()
            {
                if (canChangeAutoScroll)
                {
                    // autoscrollがOffのとき、Onにする
                    if (!enableAutoScroll)
                    {
                        EnableAutoScroll = true;
                    }
                }
                else
                {
                }
            };
            kotodama.Control.BeginInvoke(d);
        }

        #region Configuration
        //public override void Initialize()
        public void Initialize()
        {
            iComCore.Initialize();
            //base.Initialize();
            timer = new System.Windows.Forms.Timer();
            timer.Interval = 200;
            timer.Enabled = true;
            timer.Start();

            timer.Tick += new EventHandler(timer_Tick);
            kotodama.Presentation.CurrentSlide.View.ViewChanged += new EventHandler(View_ViewChanged);

            ////MultiViewSnapPluginがロードされたあとを見計らってオブジェクトを取り込む。
            //Anm.WaitAnm anm = new WaitAnm(15000);
            //anm.AnmFinishedHandler += new EventHandler(anm_AnmFinishedHandler);
            //anm.Start();

            //MultiViewSnapPluginのよみこみ。このPluginのほうがMultiViewSnapPluginよりも後にinitializeされることが前提。
            KotodamaPlugin.MultiViewSnapPlugin.MultiViewSnapPlugin plugin = (KotodamaPlugin.MultiViewSnapPlugin.MultiViewSnapPlugin)kotodama.EnvironmentTable["MultiViewSnap Plugin"];
            plugin.SnapChanged += new KotodamaPlugin.MultiViewSnapPlugin.MultiViewSnapPlugin.SnapChangedEventHandler(plugin_SnapChanged);

            //for immediate ink submit
            sendingStrokeList = new List<StrokeElement>();
            inkTimer = new System.Windows.Forms.Timer();
            inkTimer.Interval = 1000;
            inkTimer.Tick += new EventHandler(inkTimer_Tick);
            inkTimer.Enabled = true;
            inkTimer.Start();
            //kotodama.InkCollector.Stroke += new InkCollectorStrokeEventHandler(InkCollector_Stroke);
            KotodamaPlugin.ViewWritePlugin.ViewWritePlugin vwp = (KotodamaPlugin.ViewWritePlugin.ViewWritePlugin)kotodama.EnvironmentTable["ViewWrite Plugin"];
            vwp.ViewWriteMode.StrokeElementCreated += new KotodamaPlugin.ViewWritePlugin.ViewWriteMode.StrokeElementCreatedEventHandler(ViewWriteMode_StrokeElementCreated);
            vwp.ViewWriteMode.ViewMovedCreated += new KotodamaPlugin.ViewWritePlugin.ViewWriteMode.ViewMovedEventHandler(ViewWriteMode_ViewMovedCreated);

            //for ink Erase            
            vwp.ViewEraseMode.StrokeElementErased += new KotodamaPlugin.ViewWritePlugin.ViewEraseMode.StrokeElementErasedEventHandler(ViewEraseMode_StrokeElementErased);
            sendingEraseStrokeList = new List<StrokeElement>();
            
            //for overview
            overviewTimer = new System.Windows.Forms.Timer();
            overviewTimer.Interval = 10000;
            overviewTimer.Tick += new EventHandler(overviewTimer_Tick);
            overviewTimer.Enabled = true;
            overviewTimer.Start();

            kotodama.EnvironmentTable.Add("ComForDisplay.Name", Name);
        }

        private void UpdateViewSnapList()
        {
            viewSnapList = (ViewSnapList)kotodama.Presentation.CurrentSlide.ViewSnapListTable[iComCore.Name];
            if (viewSnapList == null && kotodama.Presentation.CurrentSlide.ViewSnapListTable.Count > 0)
            {
                IEnumerator enumes = kotodama.Presentation.CurrentSlide.ViewSnapListTable.Values.GetEnumerator();
                enumes.MoveNext();
                viewSnapList = (ViewSnapList)enumes.Current;
            }
        }

        void overviewTimer_Tick(object sender, EventArgs e)
        {
            if (overview)
            {
                if (kotodama.Presentation.CurrentSlide.ElementList.Count > 0)
                {
                    Rectangle r = new Rectangle(0, 0, 0, 0);
                    kotodama.Presentation.CurrentSlide.ElementList.GetBoundingBox(ref r);

                    float aspect = (float)kotodama.Presentation.CurrentSlide.View.ScreenSize.Width
                        / (float)kotodama.Presentation.CurrentSlide.View.ScreenSize.Height;
                    Rectangle re = Qurihara.QBasic.DrawingUtil.GetRectangleInAspectOf(aspect, r);

                    kotodama.Presentation.CurrentSlide.View.ViewRectangle = re;
                    kotodama.Control.Invalidate();
                }
            }
        }

        public void Dispose()
        {
            //kotodama.InkCollector.Stroke -= new InkCollectorStrokeEventHandler(InkCollector_Stroke);
            KotodamaPlugin.ViewWritePlugin.ViewWritePlugin vwp = (KotodamaPlugin.ViewWritePlugin.ViewWritePlugin)kotodama.EnvironmentTable["ViewWrite Plugin"];
            vwp.ViewWriteMode.StrokeElementCreated -= new KotodamaPlugin.ViewWritePlugin.ViewWriteMode.StrokeElementCreatedEventHandler(ViewWriteMode_StrokeElementCreated);

            KotodamaPlugin.MultiViewSnapPlugin.MultiViewSnapPlugin plugin = (KotodamaPlugin.MultiViewSnapPlugin.MultiViewSnapPlugin)kotodama.EnvironmentTable["MultiViewSnap Plugin"];
            plugin.SnapChanged -= new KotodamaPlugin.MultiViewSnapPlugin.MultiViewSnapPlugin.SnapChangedEventHandler(plugin_SnapChanged);
            
            kotodama.Presentation.CurrentSlide.View.ViewChanged -= new EventHandler(View_ViewChanged);
            timer.Tick -= new EventHandler(timer_Tick);
            iComCore.Dispose();
            //base.Dispose();
        }
        #endregion

        #region Sending

        protected void Send(string adr, string data)
        {
            iComCore.Send(iComCore.Name, "provider", adr, data);
            //iComCore.Send(iComCore.Name, "@any", adr, data); // SyncWebComCoreで対応 080411
        }
        //自動的に送られるもの
        //View送り
        private void View_ViewChanged(object sender, EventArgs e)
        {
            //能動、受動を問わず、Viewがうごいたとき

            if (sendView)// && !sendingSnap)
            {
                mutInvalidated.WaitOne();
                latestRectangle = kotodama.Presentation.CurrentSlide.View.ViewRectangle;
                invalidated = true;
                mutInvalidated.ReleaseMutex();
            }

            if (sendLog)
            {
                mutInvalidated.WaitOne();
                latestRectangleForSendLogView = kotodama.Presentation.CurrentSlide.View.ViewRectangle;
                sendLogView = true;
                mutInvalidated.ReleaseMutex();
            }

        }

        void timer_Tick(object sender, EventArgs e)
        {
            mutInvalidated.WaitOne();
            if (invalidated)
            {
                Rectangle r =latestRectangle;
                //string toSend = name + "," +
                string toSend = 
                            r.X.ToString() + "," +
                            r.Y.ToString() + "," +
                            r.Width.ToString() + "," +
                            r.Height.ToString();
                try
                {
                    Send("/view_moved", toSend);
                    //Send("/set_view", toSend);// SyncWebComCoreで対応 080411
                }
                catch (Exception ex)
                {
                    //MessageBox.Show(ex.Message);
                }
                invalidated = false;
            }
            mutInvalidated.ReleaseMutex();

            mutInvalidated.WaitOne();
            if (sendLogView)
            {
                Rectangle r = latestRectangleForSendLogView;
                //string toSend = name + "," +
                string toSend = 
                            r.X.ToString() + "," +
                            r.Y.ToString() + "," +
                            r.Width.ToString() + "," +
                            r.Height.ToString();
                try
                {
                        Send("/view__report", toSend);
                }
                catch (Exception ex)
                {
                }
                sendLogView = false;
           }
            mutInvalidated.ReleaseMutex();
        }
        //Snap送り
        private static bool sendingSnap = false;
        private void plugin_SnapChanged(object sender, KotodamaPlugin.MultiViewSnapPlugin.MultiViewSnapPlugin.SnapChangedEventArgs e)
        {
            if (sendView)
            {
                UpdateViewSnapList();
                //ViewSnapList viewSnapList = (ViewSnapList)kotodama.Presentation.CurrentSlide.ViewSnapListTable[iComCore.Name];
                if (viewSnapList != null && viewSnapList.Count > e.SnapIndex)
                {
                    mutInvalidated.WaitOne();
                    sendingSnap = true;
                    mutInvalidated.ReleaseMutex();
                    WaitAnm anm2 = new WaitAnm(2000);
                    anm2.AnmFinishedHandler += new EventHandler(anm2_AnmFinishedHandler);
                    anm2.Start();
                    try
                    {
                        Send("/go__snap", e.SnapIndex.ToString());
                    }
                    catch
                    { }
                }
            }

            //EnableAutoScroll = false;
            UpdateDelayedActivatorForAutoScroll();
        }
        private void anm2_AnmFinishedHandler(object sender, EventArgs e)
        {
            mutInvalidated.WaitOne();
            sendingSnap = false;
            mutInvalidated.ReleaseMutex();
        }

        //ImmediateInk送り
        void ViewWriteMode_StrokeElementCreated(object sender, KotodamaPlugin.ViewWritePlugin.ViewWriteMode.StrokeElementCreatedEventArgs e)
        {
            if (e.StrokeElement.Stroke.Deleted) return;

            if (sendInkImmediately)
            {
                inkTimer.Stop();
                mutInvalidated.WaitOne();
                //sendingStrokeList.Add(e.StrokeElement.Stroke);
                sendingStrokeList.Add(e.StrokeElement);
                mutInvalidated.ReleaseMutex();
                inkTimer.Start();
            }

            //EnableAutoScroll = false;
            //delayedActivatorForSync.Update();
            UpdateDelayedActivatorForAutoScroll();
        }
        //void InkCollector_Stroke(object sender, InkCollectorStrokeEventArgs e)
        //{
        //    if (e.Stroke.Deleted) return;

        //    if (sendInkImmediately)
        //    {
        //        inkTimer.Stop();
        //        mutInvalidated.WaitOne();
        //        sendingStrokeList.Add(e.Stroke);
        //        mutInvalidated.ReleaseMutex();
        //        inkTimer.Start();

        //        //Stroke s = e.Stroke;
        //        //kotodama.Presentation.CurrentSlide.View.InkScreenToInkView(ref s);
        //        //s.DrawingAttributes.Width = ViewDependentLength(
        //        //    (int)kotodama.InkCollector.DefaultDrawingAttributes.Width);
        //        //s.DrawingAttributes.Height = ViewDependentLength(
        //        //    (int)kotodama.InkCollector.DefaultDrawingAttributes.Height);

        //        //sendStrokelist.Clear();
        //        //sendStrokelist.Add(s);
        //        //SendToAllStroke(sendStrokelist);
        //        //sendStrokelist.Clear();
        //    }
        //    e.Cancel = false;
        //}

        //private int ViewDependentLength(int lengthInScreen)
        //{
        //    Point[] ps = new Point[2];
        //    ps[0] = new Point(0, 0);
        //    ps[1] = new Point(0, lengthInScreen);//,0);
        //    Qurihara.InkView view =
        //        kotodama.Presentation.CurrentSlide.View;
        //    view.InkScreenToInkView(ref ps);
        //    return ps[1].Y - ps[0].Y;
        //}
        void inkTimer_Tick(object sender, EventArgs e)
        {
            if (sendingStrokeList.Count > 0)
            {
                mutInvalidated.WaitOne();
                SendToAllStroke(sendingStrokeList);
                sendingStrokeList.Clear();
                mutInvalidated.ReleaseMutex();
            }
            if (sendingEraseStrokeList.Count > 0)
            {
                mutInvalidated.WaitOne();
                SendEraseStroke(sendingEraseStrokeList);
                sendingEraseStrokeList.Clear();
                mutInvalidated.ReleaseMutex();
            }
        }

        void ViewEraseMode_StrokeElementErased(object sender, KotodamaPlugin.ViewWritePlugin.ViewEraseMode.StrokeElementErasedEventArgs e)
        {
            //Erase信号を送信する.SendInkImmediatelyには関係なく即時送信。
            inkTimer.Stop();
            mutInvalidated.WaitOne();
            sendingEraseStrokeList.Add(e.StrokeElement);
            mutInvalidated.ReleaseMutex();
            inkTimer.Start();
            
            //EnableAutoScroll = false;
            //delayedActivatorForSync.Update();
            UpdateDelayedActivatorForAutoScroll();

        }



        //明示的に呼び出されて送られるもの
        //distribute
        public void Distribute()
        {
            ProgressBarForm pbf = new ProgressBarForm("ファイルを転送しています…");
            try
            {
                pbf.ProgressBar.Value = 50;
                pbf.Show();
                pbf.Refresh();

                KotodamaPlugin.WebIOPlugin.WebIOPlugin plugin = (KotodamaPlugin.WebIOPlugin.WebIOPlugin)kotodama.EnvironmentTable["WebIO Plugin"];
                plugin.LinkBaseDir = "";
                string savedId = plugin.SaveXmlWeb;

                //現在の設定では、アクセス先はクライアントのWebIOPlugin.ServerUrlで固定される。
                //よってpは無効。
                string p = "localhost";
                Util.GetStringProperty("Base.KotodamaWebHost", ref p);
                Send("/load", p + "," + savedId);


                ////ファイルサーバはkw.Urlで指定したもの。
                //string p = "localhost";
                //Util.GetStringProperty("Base.KotodamaWebHost", ref p);
                //clientList.Send("/load", p + "," + dat[dat.Length - 1]);

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                pbf.Hide();
                pbf.Dispose();
            }

        }
        //Ink送り
        public void SendToAllStroke(List<StrokeElement> strokeList)
        {
            if (sendToAllInk)
            {
                List<Stroke> sList = new List<Stroke>();
                foreach (StrokeElement se in strokeList) sList.Add(se.Stroke);
                string txt = InkUtil.InkSerialize(sList, InkDumpingFactor);
                string recText = RectangleSerialize(strokeList);
                Send("/draw_strokes", txt + "," + recText);

                sList.Clear();
                sList = null;
            }
        }
        //PersonalInk送り
        public void SendPersonalStroke(List<StrokeElement> strokeList)
        {
            if (sendPersonalInk)
            {
                List<Stroke> sList = new List<Stroke>();
                foreach (StrokeElement se in strokeList) sList.Add(se.Stroke);
                string txt = InkUtil.InkSerialize(sList, InkDumpingFactor);
                string recText = RectangleSerialize(strokeList);
                Send("/private__strokes", iComCore.Name + "," + txt + "," + recText);

                sList.Clear();
                sList = null;
            }
        }
        //EraseInk送り
        public void SendEraseStroke(List<StrokeElement> strokeList)
        {
            if (sendToAllInk)
            {
                string recText = RectangleSerialize(strokeList);
                Send("/erase__strokes", recText);
            }
        }
        private string RectangleSerialize(List<StrokeElement> list)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            bool first2 = true;
            foreach (StrokeElement se in list)
            {
                if (!first2) sb.Append("*");
                else first2 = false;

                Rectangle r = se.BoundingBox;
                sb.Append(r.X.ToString());
                sb.Append(":");
                sb.Append(r.Y.ToString());
                sb.Append(":");
                sb.Append(r.Width.ToString());
                sb.Append(":");
                sb.Append(r.Height.ToString());
            }
            return sb.ToString();
        }
        private List<Rectangle> RectangleDeserialize(string src)
        {
            List<Rectangle> outList = new List<Rectangle>();
            if (src == "") return outList;
            string[] recs = src.Split('*');
            foreach (string rec in recs)
            {
                string[] d = rec.Split(':');
                int x = int.Parse(d[0]);
                int y = int.Parse(d[1]);
                int w = int.Parse(d[2]);
                int h = int.Parse(d[3]);
                Rectangle r = new Rectangle(x, y, w,h);
                outList.Add(r);
            }
            return outList;
        }

        //providerはConfig Manager側で実装する。
        #region sending Role
        //public void Send_SendView(bool enable)
        //{
        //    Send("/enable__sendview", enable.ToString());
        //}
        //public void Send_CanChangeAutoScroll(bool enable)
        //{
        //    Send("/enable_canchangeautoscroll", enable.ToString());
        //}
        //public void Send_SendInkImmediately(bool enable)
        //{
        //    Send("/enable__sendinkimmediately", enable.ToString());
        //}
        //public void Send_SendToAllInk(bool enable)
        //{
        //    Send("/enable__sendtoallink", enable.ToString());
        //}
        //public void Send_SendPersonalInk(bool enable)
        //{
        //    Send("/enable_sendpersonalink", enable.ToString());
        //}
        //public void Send_ReceivePersonalInk(bool enable)
        //{
        //    Send("/enable__receivepersonalink", enable.ToString());
        //}
        ////public void Send_ShowStrokes(string name){
        ////    Send("/show_strokes", name.ToString());
        ////}
        ////public void Send_HidePersonalStrokes(){
        ////    Send("/hide__personal_strokes", "");
        ////}

        #endregion

        #endregion

        #region Receiving

        void iComCore_MessageReceived(object o, MessageEventArgs e)
        {
            mutInvalidated.WaitOne();
            receivedMessageEventQueue.Enqueue(e);
            mutInvalidated.ReleaseMutex();
            if (syncReady)
            {
                ParseEvent();
            }
        }
        void ParseEvent(){
            Delegate d = (MethodInvoker)delegate(){
                while (receivedMessageEventQueue.Count > 0)
                {
                    mutInvalidated.WaitOne();
                    MessageEventArgs ee = receivedMessageEventQueue.Dequeue();
                    mutInvalidated.ReleaseMutex();
                    ParseEvent(ee);
                }
            };
            kotodama.Control.BeginInvoke(d);
        }
        void ParseEvent(MessageEventArgs e){
            //Delegate d = (MethodInvoker)delegate()
            //{
            if (!e.DestName.Equals("@any") && !e.DestName.Equals(iComCore.Name)) return;
            string adr = e.Adr;
            string data = e.Data;
                
            if (adr.Equals("/provider"))
            {
                string[] dat = data.Split(',');
                string serverIP = dat[0];
                int serverPort = int.Parse(dat[1]);
                string oldMyName = dat[2];
                iComCore.ConnectTransmitter(serverIP, serverPort);
                //transmitter = new OSCTransmitter(serverIP, serverPort);
                if (oldMyName != iComCore.Name)
                {
                    //MessageBox.Show("display名を"+oldMyName+"に変更します。");
                    Qurihara.QBasic.TimeSwitchMessageForm tsmf = new Qurihara.QBasic.TimeSwitchMessageForm(3000);
                    tsmf.labelMessage.Text = "display名を" + oldMyName + "に変更します。";
                    tsmf.Size = new Size(300, 100);
                    tsmf.Show();
                    iComCore.Name = oldMyName;
                }
                //Host.Kotodama.Presentation.CurrentSlide.clearAll();
            }
            if (adr.Equals("/load"))
            {
                string[] dat = data.Split(',');
                string kotodamaWebServerIP = dat[0];
                string filename = dat[1];

                KotodamaPlugin.WebIOPlugin.WebIOPlugin plugin = (KotodamaPlugin.WebIOPlugin.WebIOPlugin)kotodama.EnvironmentTable["WebIO Plugin"];

                //以下の１と２はどちらも同じ。
                //1
                plugin.LinkBaseDir = "";
                plugin.LoadXmlWeb = filename;
                //2
                //plugin.LoadFile(filename);


                //送られてきた情報をもとにサーバを変更している

                //KotodamaWeb kw = new KotodamaWeb();
                //string site = kotodamaWebServerIP; 
                //kw.Url = kw.Url.Replace("//localhost/" 
                //    , "//" + site + "/"); 
                //string outs = kw.Load(filename,"pass");


                //ここでプレイリスト名を自分の名前のものにする！
                UpdateViewSnapList();
                if (viewSnapList != null && viewSnapList.Count > 0)
                {
                    //PresentationのCurrentSnapChangedがなくなって、
                    //Slideで扱えるようになって、
                    //MultiViewSnapPluginがkotodamaPanelTestでも
                    //ロード時にちゃんとリストを更新できるようになるまでの措置
                    if (kotodama.EnvironmentTable.Contains("CurrentViewSnapList"))
                    {
                        kotodama.EnvironmentTable.Remove("CurrentViewSnapList");
                    }
                    kotodama.EnvironmentTable.Add("CurrentViewSnapList", viewSnapList);

                    //((ViewSnap)viewSnapList[0]).Warp();
                    GoSnap(0);
                }


                InitPersonalInkSystem();

                kotodama.Control.Invalidate();
                //MessageBox.Show("load success");

            }

            //if (_adr.Equals(prefix + "/get_view"))
            //{
            //    string[] dat = _data.Split(',');
            //    if (name == dat[0])
            //    {
            //        if (transmitter != null)
            //        {
            //            Qurihara.View view = kotodama.Presentation.CurrentSlide.View;
            //            string toSend = name + "," +
            //                view.ViewRectangle.X.ToString() + "," +
            //                view.ViewRectangle.Y.ToString() + "," +
            //                view.ViewRectangle.Width.ToString() + "," +
            //                view.ViewRectangle.Height.ToString();
            //            Send("/current_view", toSend);
            //        }
            //    }
            //}

            if (adr.Equals("/set_view") && !sendView && snapOffset == 0 && !overview && !gooingSnap)
            {
                string[] dat = data.Split(',');
                //if (name == dat[0])
                //{
                    Rectangle destRect = new Rectangle(int.Parse(dat[0]), int.Parse(dat[1]), int.Parse(dat[2]), int.Parse(dat[3]));
                    //Rectangle destRect = new Rectangle(int.Parse(dat[1]), int.Parse(dat[2]), int.Parse(dat[3]), int.Parse(dat[4]));
                    if (enableAutoScroll)
                    {
                        kotodama.Presentation.CurrentSlide.View.ViewRectangle
                            = destRect;
                    }
                    else
                    {
                        stackedView = destRect;
                    }
                //}
            }
            if (adr.Equals("/go__snap") && !sendView && !overview) 
            {
                string[] dat = data.Split(',');
                //if (name == dat[0])
                //{
                    int snapID = int.Parse(dat[0]);
                    GoSnap(snapID);
                //}
            }
            if (adr.Equals("/view_moved"))
            {
                if (manager && ViewMovedReceived != null)
                {
                    string[] dat = data.Split(',');
                    Rectangle view = new Rectangle(
                        int.Parse(dat[0]),
                        int.Parse(dat[1]),
                        int.Parse(dat[2]),
                        int.Parse(dat[3]));

                    //e.SenderName とともにイベントかなにかを使いたいが、自分宛以外はこないことになっている。
                    ViewMovedReceived(this, new ViewMovedReceivedEventArgs(e.SenderName, view));
                }
            }

            //if (adr.Equals("/enable__sendview"))
            //{
            //    string[] dat = data.Split(',');
            //    bool enable = bool.Parse(dat[0]);
            //    sendView = enable;
            //}
            //if (adr.Equals("/enable_canchangeautoscroll"))
            //{
            //    string[] dat = data.Split(',');
            //    bool enable = bool.Parse(dat[0]);
            //    canChangeAutoScroll = enable;
            //}
            //if (adr.Equals("/enable__sendinkimmediately"))
            //{
            //    string[] dat = data.Split(',');
            //    bool enable = bool.Parse(dat[0]);
            //    sendInkImmediately = enable;
            //}
            //if (adr.Equals("/enable__sendtoallink"))
            //{
            //    string[] dat = data.Split(',');
            //    bool enable = bool.Parse(dat[0]);
            //    sendToAllInk = enable;
            //}
            //if (adr.Equals("/enable_sendpersonalink"))
            //{
            //    string[] dat = data.Split(',');
            //    bool enable = bool.Parse(dat[0]);
            //    sendPersonalInk = enable;
            //}
            //if (adr.Equals("/enable__receivepersonalink"))
            //{
            //    string[] dat = data.Split(',');
            //    bool enable = bool.Parse(dat[0]);
            //    receivePersonalInk = enable;
            //}

            if (adr.Equals("/draw_strokes"))
            {
                string[] dat = data.Split(',');
                //CreateStrokes(dat[0], "_all_");
                string recText = "";
                if (dat.Length > 1) recText = dat[1];
                CreateStrokes(dat[0],recText, "");
            }

            if (adr.Equals("/private__strokes") && receivePersonalInk)
            {
                string[] dat = data.Split(',');
                string sender = dat[0];
                string recText = "";
                if (dat.Length > 2) recText = dat[2];
                List<StrokeElement> fel = CreateStrokes(dat[1], recText, sender);
                //AddStrokeElementOwnerTable(sender,fel);
                HideElements(fel);
                UpdatePersonalInk();
            }

            if (adr.Equals("/erase__strokes"))
            {
                string[] dat = data.Split(',');
                //ストロークがあれば消す
                EraseStrokes(dat[0]);
            }

            //if (adr.Equals("/show__personal_strokes"))
            //{
            //    string[] dat = data.Split(',');
            //    string name = dat[0];
            //    if (!showingInkList.Contains(name))
            //    {
            //        showingInkList.Add(name);
            //        UpdatePersonalInk();
            //    }
            //}

            //if (adr.Equals("/hide__personal_strokes"))
            //{
            //    HideElements(GetAllStrokeElementList());
            //    showingInkList.Clear();
            //    kotodama.Control.Invalidate();
            //}
        //};
        //    kotodama.Control.BeginInvoke(d);

        }

        private bool gooingSnap = false;
        private void GoSnap(int snapID)
        {
            snapID = snapID + snapOffset;
            if (snapID < 0) snapID = 0;
            UpdateViewSnapList();
            if (viewSnapList != null && viewSnapList.Count <= snapID) snapID = viewSnapList.Count - 1;
            if (viewSnapList != null && viewSnapList.Count > snapID)
            {
                if (enableAutoScroll)
                {
                    mutInvalidated.WaitOne();
                    gooingSnap = true;
                    mutInvalidated.ReleaseMutex();
                    viewSnapList.GoLinear(snapID);
                    WaitAnm wa = new WaitAnm(2000);
                    wa.AnmFinishedHandler += new EventHandler(wa_AnmFinishedHandler);
                    wa.Start();
                }
                else
                {
                    stackedView = snapID;
                }
            }

        }

        void wa_AnmFinishedHandler(object sender, EventArgs e)
        {
            mutInvalidated.WaitOne();
            gooingSnap = false;
            mutInvalidated.ReleaseMutex();
        }

        //外部呼出し用。tabをクリックしたときにつかう。allとnameのみ提示。
        public void ShowPersonalInkOf(string name)
        {
            HideElements(GetAllStrokeElementList());
            showingInkList.Clear();
            showingInkList.Add(name);
            UpdatePersonalInk();
        }
        //外部呼出し用。tabをクリックしたときにつかう。allとすべてのpersonalinkを提示。
        public void ShowAllInk()
        {
            //HideElements(GetAllStrokeElementList());
            showingInkList.Clear();
            foreach (string name in personalInkNameList) showingInkList.Add(name);
            UpdatePersonalInk();
        }
        //外部呼出し用。ownerタブをデコードする
        public string GetOwner(StrokeElement se)
        {
            string owner = "";
            string tag = se.Tag;
            if (tag.StartsWith("owner=")){
                owner = tag.Substring(6);
            }
            return owner;
        }

        private void UpdatePersonalInk()
        {
            foreach (string name in showingInkList)
            {
                List<StrokeElement> list =
                    GetStrokeElementListByName(name);
                if (list.Count > 0)
                {
                    ShowElements(list);
                    kotodama.Control.Invalidate();
                }
            }
        }
        private List<string> showingInkList = new List<string>();
        private List<StrokeElement> GetStrokeElementListByName(string name)
        {
            List<StrokeElement> list = new List<StrokeElement>();
            foreach (FigureElement fe in kotodama.Presentation.CurrentSlide.ElementList)
            {
                if (fe is StrokeElement)
                {
                    StrokeElement se = fe as StrokeElement;
                    if (se.Tag.Equals("owner="+name))
                    {
                        list.Add(se);
                    }

                }
            }
            return list;
        }
        private List<StrokeElement> GetAllStrokeElementList()
        {
            List<StrokeElement> list = new List<StrokeElement>();
            foreach (FigureElement fe in kotodama.Presentation.CurrentSlide.ElementList)
            {
                if (fe is StrokeElement)
                {
                    StrokeElement se = fe as StrokeElement;
                    if (se.Tag.StartsWith("owner="))
                    {
                        list.Add(se);
                    }

                }
            }
            return list;

        }
        //private List<StrokeElement> allList = new List<StrokeElement>();
        private List<string> personalInkNameList = new List<string>();
        private void InitPersonalInkSystem()
        {
            personalInkNameList.Clear();
            showingInkList.Clear();
            //foreach (List<StrokeElement> sl in strokeElementOwnerTable)
            //{
            //    sl.Clear();
            //}
            //strokeElementOwnerTable.Clear();
            //strokeElementOwnerTable.Add("_all_", allList);

        }
        //private Hashtable strokeElementOwnerTable = new Hashtable();
        //private void AddStrokeElementOwnerTable(string sender, List<StrokeElement> fel){
        //    List<StrokeElement> sl=null;
        //    if (strokeElementOwnerTable.ContainsKey(sender))
        //    {
        //        sl = (List<StrokeElement>)strokeElementOwnerTable[sender];
        //    }
        //    else
        //    {
        //        sl = new List<StrokeElement>();
        //        strokeElementOwnerTable.Add(sender,sl);
        //    }
        //    foreach (StrokeElement se in fel)
        //    {
        //        sl.Add(se);
        //        allList.Add(se);
        //    }
        //}

        private static void HideElements(List<StrokeElement> fel)
        {
            foreach (StrokeElement se in fel)
            {
                se.Findable = false;
                se.Visible = false;
                //se.Editable = false;
            }
        }
        private static void ShowElements(List<StrokeElement> fel)
        {
            foreach (StrokeElement se in fel)
            {
                se.Findable = true;
                se.Visible = true;
                //se.Editable = true;
            }
        }
        private List<StrokeElement> createdStrokeElementList = new List<StrokeElement>();

        private List<StrokeElement> CreateStrokes(string srcInk, string srcRect,string owner)
        {
            createdStrokeElementList.Clear();
            while (kotodama.InkCollector.CollectingInk) { }
            List<Stroke> list = InkUtil.InkDeserialize(kotodama.InkCollector.Ink, srcInk);
            List<Rectangle> rList = RectangleDeserialize(srcRect);

            if (list.Count == rList.Count)
            {
                for (int i = 0; i < list.Count; i++)
                {
                    Stroke s = list[i];
                    Rectangle re = rList[i];
                    FigureElement fe = kotodama.Presentation.CurrentSlide.createStroke(-1, re, s);
                    ((StrokeElement)fe).SynchronizeBoundingBox();
                    if (owner != "")
                    {
                        fe.Tag = "owner=" + owner;
                        if (!personalInkNameList.Contains(owner))
                        {
                            personalInkNameList.Add(owner);
                            if (NewPersonalInkNameReveived != null) NewPersonalInkNameReveived(this, new NewPersonalInkNameReceivedEventArgs(owner));
                        }
                    }
                    createdStrokeElementList.Add((StrokeElement)fe);
                    kotodama.Control.Invalidate();

                }
            }
            else
            {
                foreach (Stroke stroke in list)
                {
                    Rectangle re;
                    re = new Rectangle(stroke.GetBoundingBox().Location, stroke.GetBoundingBox().Size);
                    InkUtil.GetBoundingBoxFromInk(kotodama.Control, ref re, kotodama.InkCollector);
                    //InkUtil.GetViewBoundingBoxFromInk(kotodama.Control, ref re, kotodama.InkCollector,kotodama.Presentation.CurrentSlide.View);
                    Stroke s = stroke;
                    //kotodama.Presentation.CurrentSlide.View.InkScreenToInkView(ref s);
                    FigureElement fe = kotodama.Presentation.CurrentSlide.createStroke(-1, re, s);
                    ((StrokeElement)fe).SynchronizeBoundingBox();
                    if (owner != "")
                        fe.Tag = "owner=" + owner;
                    createdStrokeElementList.Add((StrokeElement)fe);
                    //fe.Invalidate();
                    //Host.Kotodama.Control.Invalidate(fe.BoundingBox);
                    kotodama.Control.Invalidate();
                }
            }
            if (createdStrokeElementList.Count > 0 && StrokeElementListCreated != null)
                StrokeElementListCreated(this,new StrokeElementListCreatedEventArgs(createdStrokeElementList));
            return createdStrokeElementList;
        }

        private void EraseStrokes(string srcRect)
        {
            createdStrokeElementList.Clear();
            while (kotodama.InkCollector.CollectingInk) { }
            List<Rectangle> rList = RectangleDeserialize(srcRect);

            FigureElementList eList = new FigureElementList();
            FigureElementList eeList = new FigureElementList();
            foreach (FigureElement se in kotodama.Presentation.CurrentSlide.ElementList)// createdStrokeElementList)
            {
                if (se is StrokeElement)
                    eList.Add(se);
            }

            for (int i = 0; i < rList.Count; i++)
            {
                Rectangle re = rList[i];
                //SynchronizeBoundingBoxでずれる可能性はある。
                //FigureElement ee = eList.getNearestElement((re.Left + re.Right) / 2, (re.Top + re.Bottom) / 2);
                //eList.Clear();
                //eList.Add(ee);
                //kotodama.Presentation.CurrentSlide.EraseObjects(eList);

                foreach (StrokeElement se in eList)
                {
                    if (IsRectanglesNear(se.BoundingBox, re))
                        eeList.Add(se);
                }

                //foreach (StrokeElement se in createdStrokeElementList)
                //{
                //    if (
                //        //se.Tag.StartsWith("owner=" + owner) &&  //はじめはすべて消去
                //        //se.BoundingBox.Equals(re)
                //        IsRectanglesNear(se.BoundingBox,re)
                //        )
                //    {
                //        eList.Add(se);
                //    }
                //}
                //kotodama.Presentation.CurrentSlide.EraseObjects(eList);


            }
            kotodama.Presentation.CurrentSlide.EraseObjects(eeList);

            //foreach (FigureElement fe in eeList)
            //{
            //    createdStrokeElementList.Remove((StrokeElement)fe);
            //}
            eList.Clear();
            eList = null;
            eeList.Clear();
            eeList = null;

            //createdStrokeElementList.Add((StrokeElement)fe);
            kotodama.Control.Invalidate();


            // もしかしたら以下のようなイベントを発火すべきかも。manager用？
            //if (createdStrokeElementList.Count > 0 && StrokeElementListCreated != null)
            //    StrokeElementListCreated(this, new StrokeElementListCreatedEventArgs(createdStrokeElementList));

        }

        private static int Near = 4;
        private bool IsRectanglesNear(Rectangle a, Rectangle b)
        {
            return (
                Math.Abs(a.Left - b.Left) < Near &&
                Math.Abs(a.Top - b.Top) < Near &&
                Math.Abs(a.Width - b.Width) < Near &&
                Math.Abs(a.Height - b.Height) < Near
                );
        }

        #endregion

    }
}
